package controllers

import (
	"fmt"
	"strconv"

	"beego-api-demo/models"
	"beego-api-demo/models/myredis"

	"github.com/astaxie/beego"
	"github.com/dgrijalva/jwt-go"
)

type BooksController struct {
	BaseController
}

func (c *BooksController) Post() {
	ctlErr := checkPermission(c)
	if ctlErr != nil {
		c.RetError(ctlErr, "CheckPermission:", nil)
		return
	}

	form := models.BookPostForm{}
	ctlErr = c.parseRequestBodyAndValidate(&form)
	if ctlErr != nil {
		c.RetError(ctlErr, "ParseBookPost:", nil)
		return
	}

	book := models.NewBook(&form)
	_, err := book.Insert()
	if err != nil {
		c.RetError(errDatabase, "InsertBook:", err)
		return
	}

	c.RetOk(&models.BookInfo{BookInfo: book})
}

func (c *BooksController) GetOne() {
	ctlErr := checkPermission(c)
	if ctlErr != nil {
		c.RetError(ctlErr, "CheckPermission:", nil)
		return
	}

	id := getRequestBookId(c)

	book := models.Book{}
	err := myredis.ReadStruct(fmt.Sprintf("book.id.%d", id), &book)
	if err == nil {
		beego.Debug("ReadFromCache:", strconv.FormatInt(id, 10))
		c.RetOk(&models.BookInfo{BookInfo: &book})
		return
	}

	code, err := book.FindByID(id)
	if err != nil {
		if code == models.ErrNotFound {
			c.RetError(errNoBook, "FindBookByIdButNoFound:", err)
		} else {
			c.RetError(errDatabase, "FindBookById:", err)
		}
		return
	}

	myredis.WriteStruct(fmt.Sprintf("book.id.%d", id), book)

	c.RetOk(&models.BookInfo{BookInfo: &book})
}

func (c *BooksController) GetAll() {
	ctlErr := checkPermission(c)
	if ctlErr != nil {
		c.RetError(ctlErr, "CheckPermission:", nil)
		return
	}

	queryVal, queryOp, err := c.ParseQueryParm()
	order, err := c.ParseOrderParm()
	limit, err := c.ParseLimitParm()
	offset, err := c.ParseOffsetParm()

	Books, err := models.GetAllBooks(queryVal, queryOp, order, limit, offset)
	if err != nil {
		c.RetError(errDatabase, "GetAllBook:", err)
		return
	}

	c.RetOk(&models.BooksInfo{BookInfo: Books})
}

func (c *BooksController) Put() {
	ctlErr := checkPermission(c)
	if ctlErr != nil {
		c.RetError(ctlErr, "CheckPermission:", nil)
		return
	}

	id := getRequestBookId(c)

	form := models.BookPutForm{}
	ctlErr = c.parseRequestBodyAndValidate(&form)
	if ctlErr != nil {
		c.RetError(ctlErr, "ParseBookPut:", nil)
		return
	}

	book := models.Book{}
	if code, err := book.UpdateByID(id, &form); err != nil {
		c.RetError(errDatabase, "UpdateBookById:", err)
		return
	} else if code == models.ErrNotFound {
		c.RetError(errNoBookChange, "UpdateBookByIdButNotFound:", err)
		return
	}

	if _, err := book.FindByID(id); err != nil {
		c.RetError(errDatabase, "FindUpdatedBookById:", err)
		return
	}

	c.RetOk(&models.BookInfo{BookInfo: &book})
}

func (c *BooksController) Delete() {
	ctlErr := checkPermission(c)
	if ctlErr != nil {
		c.RetError(ctlErr, "CheckPermission:", nil)
		return
	}

	id := getRequestBookId(c)

	book := models.Book{}
	if code, err := book.DeleteByID(id); err != nil {
		c.RetError(errDatabase, "DeleteBookById:", err)
		return
	} else if code == models.ErrNotFound {
		c.RetError(errNoBook, "DeleteBookByIdButNoFound:", err)
		return
	}
}

func checkPermission(c *BooksController) (err *ControllerError) {
	token, e := c.ParseToken()
	if e != nil {
		return e
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if ok {
		if claims["id"] == nil {
			return errPermission
		}
	} else {
		return errPermission
	}

	return nil
}

func getRequestBookId(c *BooksController) (id int64) {
	idStr := c.Ctx.Input.Param(":id")
	id, err := strconv.ParseInt(idStr, 0, 64)
	if err != nil {
		id = -1
	}
	return
}
